/* -*- P4_16 -*- */
#include <core.p4>
#include <v1model.p4>

/****************************** Constants ********************************/
const bit<16> TYPE_IPV4 = 0x800;  // IPv4 以太网类型
const bit<16> TYPE_ARP  = 0x806;  // ARP 以太网类型
const bit<16> ARP_REQUEST = 1;    // ARP 请求操作码
const bit<16> ARP_REPLY   = 2;    // ARP 响应操作码
const bit<16> HW_TYPE_ETHERNET = 1; // 硬件类型：以太网
const bit<8>  MAC_ADDR_LEN = 6;   // MAC 地址长度
const bit<8>  IPV4_ADDR_LEN = 4;  // IPv4 地址长度

/****************************** Headers *********************************/
typedef bit<9>  egressSpec_t;
typedef bit<48> macAddr_t;
typedef bit<32> ip4Addr_t;

header ethernet_t {
    macAddr_t dstAddr;
    macAddr_t srcAddr;
    bit<16>   etherType;
}

header ipv4_t {
    bit<4>    version;
    bit<4>    ihl;
    bit<8>    diffserv;
    bit<16>   totalLen;
    bit<16>   identification;
    bit<3>    flags;
    bit<13>   fragOffset;
    bit<8>    ttl;
    bit<8>    protocol;
    bit<16>   hdrChecksum;
    ip4Addr_t srcAddr;
    ip4Addr_t dstAddr;
}

header arp_t {
    bit<16> hwType;          // 硬件类型
    bit<16> protoType;       // 协议类型
    bit<8>  hwAddrLen;       // 硬件地址长度
    bit<8>  protoAddrLen;    // 协议地址长度
    bit<16> opcode;          // 操作码
    macAddr_t senderHwAddr;  // 发送方MAC
    ip4Addr_t senderProtoAddr; // 发送方IP
    macAddr_t targetHwAddr;  // 目标MAC
    ip4Addr_t targetProtoAddr; // 目标IP
}

/****************************** Metadata ********************************/
struct metadata {
    /* 可添加自定义元数据字段 */
}

struct headers {
    ethernet_t   ethernet;
    ipv4_t       ipv4;
    arp_t        arp;
}

/****************************** Parser **********************************/
parser MyParser(packet_in packet,
                out headers hdr,
                inout metadata meta,
                inout standard_metadata_t standard_metadata) {

    state start {
        transition parse_ethernet;
    }

    state parse_ethernet {
        packet.extract(hdr.ethernet);
        transition select(hdr.ethernet.etherType) {
            TYPE_IPV4: parse_ipv4;
            TYPE_ARP:  parse_arp;
            default:   accept;
        }
    }

    state parse_ipv4 {
        packet.extract(hdr.ipv4);
        transition accept;
    }

    state parse_arp {
        packet.extract(hdr.arp);
        transition accept;
    }
}

/*************************** Verify Checksum ****************************/
control MyVerifyChecksum(inout headers hdr, inout metadata meta) {
    apply { }
}

/*************************** Ingress Processing *************************/
control MyIngress(inout headers hdr,
                  inout metadata meta,
                  inout standard_metadata_t standard_metadata) {

    action drop() {
        mark_to_drop(standard_metadata);
    }

    action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) {
        standard_metadata.egress_spec = port;
        hdr.ethernet.srcAddr = hdr.ethernet.dstAddr;
        hdr.ethernet.dstAddr = dstAddr;
        hdr.ipv4.ttl = hdr.ipv4.ttl - 1;
    }

    action generate_arp_reply(macAddr_t switch_mac) {
        // 交换MAC地址
        hdr.ethernet.srcAddr = switch_mac;
        hdr.ethernet.dstAddr = hdr.arp.senderHwAddr;

        // 构造ARP响应
        hdr.arp.opcode = ARP_REPLY;
        hdr.arp.targetHwAddr = hdr.arp.senderHwAddr;
        hdr.arp.targetProtoAddr = hdr.arp.senderProtoAddr;
        hdr.arp.senderHwAddr = switch_mac;
        hdr.arp.senderProtoAddr = hdr.arp.targetProtoAddr;
    }

    table ipv4_lpm {
        key = {
            hdr.ipv4.dstAddr: lpm;
        }
        actions = {
            ipv4_forward;
            drop;
            NoAction;
        }
        size = 1024;
        default_action = drop();
    }

    table arp_table {
        key = {
            hdr.arp.targetProtoAddr: exact;
            hdr.arp.opcode: exact;
        }
        actions = {
            generate_arp_reply;
            NoAction;
        }
        size = 1024;
        default_action = NoAction();
    }

    apply {
        if (hdr.arp.isValid()) {
            // 只处理发往本机的ARP请求
            if (hdr.arp.opcode == ARP_REQUEST) {
                arp_table.apply();
            }
        }
        else if (hdr.ipv4.isValid()) {
            ipv4_lpm.apply();
        }
    }
}

/*************************** Egress Processing **************************/
control MyEgress(inout headers hdr,
                 inout metadata meta,
                 inout standard_metadata_t standard_metadata) {
    apply { }
}

/************************** Compute Checksum ****************************/
control MyComputeChecksum(inout headers hdr, inout metadata meta) {
    apply {
        update_checksum(
            hdr.ipv4.isValid(),
            { hdr.ipv4.version,
              hdr.ipv4.ihl,
              hdr.ipv4.diffserv,
              hdr.ipv4.totalLen,
              hdr.ipv4.identification,
              hdr.ipv4.flags,
              hdr.ipv4.fragOffset,
              hdr.ipv4.ttl,
              hdr.ipv4.protocol,
              hdr.ipv4.srcAddr,
              hdr.ipv4.dstAddr },
            hdr.ipv4.hdrChecksum,
            HashAlgorithm.csum16);
    }
}

/***************************** Deparser *********************************/
control MyDeparser(packet_out packet, in headers hdr) {
    apply {
        packet.emit(hdr.ethernet);
        packet.emit(hdr.arp);
        packet.emit(hdr.ipv4);
    }
}

/***************************** Switch ***********************************/
V1Switch(
    MyParser(),
    MyVerifyChecksum(),
    MyIngress(),
    MyEgress(),
    MyComputeChecksum(),
    MyDeparser()
) main;
